home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Cream of the Crop 3
/
Cream of the Crop 3.iso
/
utility
/
csap421.zip
/
CSAP.C
next >
Wrap
C/C++ Source or Header
|
1994-02-25
|
20KB
|
603 lines
/*
**************************** NOTICE! **************************
* Contrary to the current trend in MS-DOS software this *
* program, for whatever it is worth, is NOT copyrighted *
* (with the exception of the runtime library from Borland *
* International's Turbo C)! The program, in whole or in *
* part, may be used freely in any fashion or environment *
* desired. If you find this program to be useful to you, *
* do NOT send any contribution to the author; in the words *
* of Rick Conn, 'Enjoy!' However, if you make any *
* improvements, I would enjoy receiving a copy of the *
* modified source. I can be reached at the following: *
* *
* on CompuServ: 70410,1004 *
* on Channel 1 BBS (xxx) xxx-xxx *
* or by mail or phone: *
* *
* Don A. Williams *
* 3913 W. Solano Dr. N. *
* Phoenix, AZ 85019 *
* (602) 841-5333 *
* *
* Every effort has been made to avoid error and moderately *
* extensive testing has been performed on this program, *
* however, the author does not warrant it to be fit for any *
* purpose or to be free from error and disclaims any *
* liability for actual or any other damage arising from the *
* use of this program. *
* *
* Calls to absread/abswrite modified for DOS 4+ and large *
* disk partition sizes *
* *
* Edgar W. Swank *
* 5515 Spinnaker Dr., #4 *
* San Jose, CA 95123 *
* (408)227-3471 *
* Internet: edgar@spectrx.saigon.com *
*****************************************************************
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dos.h>
#include <ctype.h>
#include <dir.h>
#include <mem.h>
#include <alloc.h>
#define MAIN
#include "dosstruc.h"
/*--- Function Prototypes ---*/
void Usage(void);
void GetDPB(int Disk, struct DpbStruct * Dpb);
unsigned GetDosVersion(void);
char *strrspn(char *s1, char *s2);
int AbortProgram(void);
void SortDir(void);
void INT3(void);
/*--- End of Prototypes ---*/
#define MAX12BIT 0x0FF6
#define MAX16BIT 0xFFF6
#if defined(__TINY__)
#define MODEL "Tiny"
#elif defined(__SMALL__)
#define MODEL "Small"
#elif defined(__COMPACT__)
#define MODEL "Compact"
#elif defined(__MEDIUM__)
#define MODEL "Medium"
#elif defined(__LARGE__)
#define MODEL "Large"
#elif defined(__HUGE__)
#define MODEL "Huge"
#endif
char Disk; /* Alpha working disk ('A', 'B', .... ) */
char CurDir[67]; /* Storage for Current Directory of disk */
char Path[67]; /* Storage for Path to sort */
char Parent[67]; /* Storage for Parent part of Path */
char Element[13]; /* Storage for Child part of Path */
char Line[80]; /* Working storage for strings */
char Order = 'N'; /* Sort key indicator (Default=Name/Ext) */
char Inverse = 0; /* Ascend/Descend indic. (Default=Ascend) */
char Level = 0; /* Recursive sort indic. (default=Recursive) */
char RSwt = 0; /* Report switch (Default=No Report) */
char VerSwt = 0; /* Pause for operator verify (Default=off) */
char Packed = 1; /* Elim. "erased" entries (Default=on) */
char TruncateSwt = 0;/* Truncate directories (Default=off) */
char FatDirty = 0; /* FAT needs to be rewritten */
int Is12Bit; /* 12 / 16 bit cluster indicator */
int *CluArray; /* Cluster Array ptr, dynamically allocated */
int Lim, i, j, k, l;
long m;
int OutSectors, OutClusters, BytesPerCluster, ECount;
unsigned LastCluster; /* Value for end of cluster chain */
unsigned Cluster, Sector, NumSec;
unsigned FatSize;
long FatRecSize;
short FatRecCount;
struct FatStruct {
unsigned char *Ptr;
unsigned Size;
} *FatArray;
struct absr32m a32;
unsigned Version;
long MinMem; /* Minimum available memory */
unsigned Freed = 0; /* Freed cluster count */
unsigned Version;
unsigned DirStart;
struct DpbStruct Dpb; /* Disk Parameter Block (see dosstruc.h) */
struct ClusterQueue CluQ; /* Queue of cluster for directory */
struct DirEntry *DirBuff; /* Buffer for directory to be sorted */
struct ExtendedEntry Dir;
struct ClusterEntry *p, *t;
struct ExtFcb Fcb;
void
main (int argc, char *argv[]) {
char *strrspn();
void SortDir(), Usage();
char *p, *p1, t1;
int i, j;
bdos(0x0D, 0, 0); /* Reset Disk Subsystem - Flush all buffers */
fputs("C-Sort And Pack [CSAP]: Version 4.2.1: Date: February 25, 1994", stderr);
fputs(" [", stderr);
fputs(MODEL, stderr);
fputs(" Model]\n", stderr);
fputs(" use \"CSAP -H\" or \"CSAP ?\" for help.\n\n", stderr);
Disk = getdisk() + 'A';
Line[0] = '\\';
getcurdir(Disk - '@', &Line[1]);
ctrlbrk(AbortProgram); /* Install "wrap-up" in Control Break vec. */
/* Interpret command line arguments, if any */
for (i = 1; i < argc; ++i) {
if (argv[i][0] == '-') {
for (j = 1; j < strlen(argv[i]); ++j) {
switch (toupper(argv[i][j])) {
case 'N': /* Sort Key = Name/Ext (default) */
Order = 'N';
break;
case 'D': /* Sort Key = Date/Time */
Order = 'D';
break;
case 'E': /* Sort Key = Ext/Name */
Order = 'E';
break;
case 'S': /* Sort Key = File Size */
Order = 'S';
break;
case 'R': /* Report Dir loc. & "erased" */
RSwt = 1;
break;
case 'I': /* Sort order inverse */
Inverse = 1;
break;
case 'P': /* Do NOT remove "erased" entries */
Packed = 0;
break;
case 'L': /* Limit sort to one level */
Level = 1;
break;
case 'V': /* Request approval before sort */
VerSwt = 1;
break;
case 'T': /* Truncate directories */
TruncateSwt = 1;
break;
case 'H':
Usage();
default: /* Illegal option */
fprintf(stderr, "Invalid option %s.\n", argv[i]);
Usage();
break;
}
}
}
else { /* Not switch, assume directory name or '?' */
if (argv[i][0] == '?') Usage();
if (argv[i][1] == ':') { /* Check for disk specified */
Disk = toupper(argv[i][0]);
p = &argv[i][2];
}
else p = &argv[i][0];
if ((p[0] != '\\') && (p[0] != '/')) {
Line[0] = '\\';
getcurdir(Disk - '@', &Line[1]);
p1 = &p[strcspn(p, "\\/")];
t1 = *p1;
*p1 = '\0';
if (!strcmp(p, ".")) {
p = p1;
*p1 = t1;
}
else if (!strcmp(p, "..")) {
while (!strcmp(p, "..")) {
p = strrspn(Line, "\\/");
if ((p - Line) == 0) ++p;
*p = '\0';
if (t1 != '\0') p = ++p1;
else p = p1;
p1 = &p[strcspn(p, "\\/")];
t1 = *p1;
*p1 = '\0';
}
*p1 = t1;
}
else *p1 = t1;
if (*p != '\0') strcat(Line, "\\");
strcat(Line, p);
}
else strcpy(Line, p);
}
}
/*
* Get disk information - uses un-documented DOS call, Int 21H, Func.
* 32H This function has been verified to work correctly in PC/MS-DOS
* versions 2.0 through 3.3. It is heavily used by DOS programs such
* as CHKDSK.
*/
GetDPB(Disk, &Dpb);
Version = GetDosVersion() & 0xFF;
switch (Version) {
case 2:
FatSize = Dpb.V.V2.FatSize;
DirStart = Dpb.V.V2.DirStart;
break;
case 3:
FatSize = Dpb.V.V3.FatSize;
DirStart = Dpb.V.V3.DirStart;
break;
case 4:
case 5:
case 6:
FatSize = Dpb.V.V4.FatSize;
DirStart = Dpb.V.V4.DirStart;
break;
default:
fprintf(stderr, "Invalid DOS version: %d\n", Version);
exit(1);
}
FatRecSize = (long) FatSize * Dpb.SectorSize;
/* Establish whether disk has 16-bit or 12-bit clusters */
if (Dpb.LastCluster > MAX16BIT) {
fprintf(stderr, "Sorry, CSAP does not yet support FAT entries > 16 bits.\n");
exit(1);
}
Is12Bit = (Dpb.LastCluster > MAX12BIT) ? 0 : 1;
LastCluster = (Is12Bit) ? 0x0FF8 : 0xFFF8;
/*
* Get & save current directory of working disk. We have to change
* to sort and must restore on termination
*/
CurDir[0] = Disk;
CurDir[1] = ':';
CurDir[2] = '\\';
getcurdir(Disk - '@', (char *) &CurDir[3]);
/* Allocate space to hold entire FAT in memory and read it in */
FatRecCount = ((FatRecSize + (long) (FAT_BLK - 1)) / FAT_BLK); /* EWS */
if ((FatArray = malloc(FatRecCount * sizeof(struct FatStruct))) == NULL) {
fprintf(stderr, "Insufficient memory for FAT array.\n");
exit(1);
}
for (m = FatRecSize, i = 0; i < FatRecCount; i++, m -= (m > FAT_BLK) ? FAT_BLK : m) {
if ((FatArray[i].Ptr = malloc((m > FAT_BLK) ? FAT_BLK : (unsigned) m)) == NULL) {
fprintf(stderr, "Insufficient memory for FAT Array entry.\n");
exit(1);
}
FatArray[i].Size = (m > FAT_BLK) ? FAT_BLK : (unsigned) m;
}
for (i = 0, m = Dpb.FatStart; i < FatRecCount; m += FatArray[i].Size / Dpb.SectorSize, i++) {
if (absread(Disk - 'A', FatArray[i].Size / Dpb.SectorSize,
(unsigned) m, FatArray[i].Ptr) != 0) {
#if 0
a32.nsect = FatArray[i].Size / Dpb.SectorSize;
a32.sector = m;
a32.xferad = FatArray[i].Ptr;
if (absread(Disk - 'A', -1, 0, &a32) != 0) { /* EWS */
#endif
fprintf(stderr, "Error reading FAT.\n");
perror("");
exit(1);
}
}
/*
* Develop full path name for directory to be sorted and separate
* into Parent and Child portions
*/
Path[0] = Parent[0] = Element[0] = '\0';
Path[0] = Disk;
Path[1] = ':';
Path[2] = '\0';
if ((Line[0] != '\\') && (Line[0] != '/')) {
strcat(Path, "\\");
strcpy(&Path[3], &CurDir[3]);
if ((Path[strlen(Path) - 1] != '\\') && (Path[strlen(Path) - 1] != '/'))
strcat(Path, "\\");
}
strcat(Path, Line);
p = strrspn(Path, "\\/");
strcpy(Element, &p[1]);
if (p[-1] == ':') p++;
strncpy(Parent, Path, (int) (p - Path));
Parent[(int) (p - Path)] = '\0';
MinMem = coreleft(); /* Initialize minimum available memory */
/*
* Perform sort. SortDir is recursive and, if Level is not on, will
* sort sort all levels of the hierarchy from the starting level down
*/
SortDir();
printf("Minimum memory= %ld\n", MinMem);
bdos(0x0D, 0, 0); /* Reset disk subsystem - flush all buffers */
if (FatDirty) {
for (i = 0, m = Dpb.FatStart; i < FatRecCount; m += FatArray[i].Size / Dpb.SectorSize, i++) {
if (abswrite(Disk - 'A', FatArray[i].Size / Dpb.SectorSize, m, FatArray[i].Ptr) != 0) {
fprintf(stderr, "Error writing FAT.\n");
perror("");
exit(1);
}
}
if (Dpb.FatCopies == 2) {
for (i = 0, m = Dpb.FatStart + FatSize; i < FatRecCount; m += FatArray[i].Size / Dpb.SectorSize, i++) {
if (abswrite(Disk - 'A', FatArray[i].Size / Dpb.SectorSize, m, FatArray[i].Ptr) != 0) {
fprintf(stderr, "Error writing 2nd copy of FAT.\n");
perror("");
exit(1);
}
}
}
printf("There %s %d cluster%s (%d bytes) freed\n",
(Freed == 1) ? "was" : "were",
Freed,
(Freed == 1) ? "" : "s",
Freed * Dpb.SectorSize * (Dpb.ClusterSize + 1));
}
bdos(0x0D, 0, 0); /* Reset disk subsystem - flush all buffers */
bdosptr(0x3B, CurDir, 0); /* Restore input "current" directory */
} /* end Main */
/*
* STRRSPN is simply a reverse version of STRSPN. It finds the LAST
* occurance in S1 of any member of S2. For some reason, none of the C
* compilers that I use provide this although they all provide STRSPN
*/
char *
strrspn (char *s1, char *s2) {
char *p1;
p1 = s1 + strlen(s1) - 1;
while (p1 >= s1) {
if (strchr(s2, *p1) != NULL) return (p1);
--p1;
}
return ((char *) NULL);
}
/*
* SearchFirst -- Search for First Directory Entry. On entry fcb contains an
* extended File Control Block with file name and attribute bits set. On
* exit, fcb contains matched entry unless return code is 255, in which case
* no match was found. This routine is used instead of the ones provided by
* the compiler so that the cluster information for the directory can be
* obtained.
*/
int
SearchFirst (struct ExtFcb * Fcb) {
union REGS regs;
regs.x.ax = 0x1100;
regs.x.dx = (unsigned) Fcb;
intdos(®s, ®s);
return ((int) (regs.x.ax & 0xFF));
}
/*
* Alu2Sec -- Converts an input cluster number [ALU] into the disk-relative
* sector for use with DOS Absolute Disk Read [interrupt 25H] or Absolute
* Disk Write [interrupt 26H]. Requires access to the undocumented DOS Disk
* Parameter Block [use funtion GetDPB].
*/
long
Alu2Sec (struct DpbStruct * Dpb, unsigned Alu) {
return ((long) (Alu - 2) * (Dpb->ClusterSize + 1) + Dpb->DataStart);
}
/*
* NextCl -- This function calculates the logical "chaining" of cluster
* numbers in a File Allocation Table [FAT]. Given an entry cluster number
* it calculates the next cluster using the array Fat[].
*
* If Is12Bit is TRUE then Fat[] is assumed to contain 12 bit entries, otherwise
* Fat[] is assumed to contain 16 bit entries.
*/
unsigned
NextCl (int Is12Bit, unsigned Cluster) {
unsigned ClWord, ClOffset;
long Factor = FAT_BLK / 2;
if (Is12Bit) { /* 12 bit FAT lookup */
ClOffset = 3 * Cluster / 2;
ClWord = (FatArray[ClOffset / FAT_BLK].Ptr[ClOffset % FAT_BLK] & 0xFF)
+ (FatArray[(ClOffset + 1) / FAT_BLK].Ptr[(ClOffset + 1) % FAT_BLK] << 8);
if (Cluster & 1) return (ClWord >> 4); /* odd cluster */
else return (ClWord & 0x0FFF); /* even cluster */
}
else
return (((unsigned int *) (FatArray[Cluster / Factor].Ptr))[Cluster % Factor]);
}
void
FreeCluster (int Is12Bit, unsigned Val, unsigned Cluster) {
extern char FatDirty;
extern unsigned Freed;
unsigned ClWord, ClOffset;
if (Is12Bit) { /* 12 bit FAT lookup */
ClOffset = 3 * Cluster / 2;
ClWord = FatArray[ClOffset / FAT_BLK].Ptr[ClOffset % FAT_BLK]
+ (FatArray[(ClOffset + 1) / FAT_BLK].Ptr[(ClOffset + 1) % FAT_BLK] << 8);
if (Cluster & 1) ClWord = (ClWord & 0xF) | (Val & 0xFFF0); /* odd */
else ClWord = (ClWord & 0xF000) | (Val & 0x0FFF);
FatArray[(ClOffset + 1) / FAT_BLK].Ptr[(ClOffset + 1) % FAT_BLK] = ClWord >> 8;
FatArray[ClOffset / FAT_BLK].Ptr[ClOffset % FAT_BLK] = ClWord & 0xFF;
}
else
((unsigned int *) FatArray[Cluster / INT_FAT_BLK].Ptr)[Cluster % INT_FAT_BLK] = Val; /* 16 bit FAT lookup */
if (!Val) ++Freed;
}
/*
* PutQueue -- Builds a simple FIFO linked list using dynamically acquired
* memory.
*/
void
PutQueue (struct ClusterQueue * Q, unsigned Cluster) {
struct ClusterEntry *p;
if ((p = malloc(sizeof(struct ClusterEntry))) == NULL) {
fprintf(stderr, "Insufficient memory(1).\n");
AbortProgram();
}
p->Next = NULL;
p->Cluster = Cluster;
if (Q->Head == NULL) Q->Head = p;
else Q->Current->Next = p;
Q->Current = p;
Q->Count++;
}
/*
* AbortProgram -- Aborts the program, resetting the current directory, with
* an error code of 1.
*/
int
AbortProgram (void) {
bdos(0x0D, 0, 0); /* Reset disk subsystem - flush all buffers */
bdosptr(0x3B, CurDir, 0); /* Reset input Current Directory */
exit(1);
return (0);
}
/*
* strincmp -- The comparsion routine for the qsort algorithm.
*/
int
strincmp (struct DirEntry * a, struct DirEntry * b) {
long t;
/*
* Ensure that "erased" entries sort high no matter what the sort key
* is.
*/
if ((a->Name[0] == 0xE5) && (b->Name[0] != 0xE5)) return (1);
if (b->Name[0] == 0xE5) return (-1);
/*
* Ensure that directories sort lower than files no matter what sort
* key
*/
if ((a->Name[0] != 0xE5) && (b->Name[0] != 0xE5)) {
if ((a->Attribute & 0x10) ^ (b->Attribute & 0x10)) {
if (a->Attribute & 0x10) return (-1);
else return (1);
}
}
/* Actual sort key compare routines */
switch (Order) {
case 'D': /* Sort key is Date/Time */
if (a->ModifyDate < b->ModifyDate) t = -1;
else if (a->ModifyDate > b->ModifyDate) t = 1;
else {
if (a->ModifyTime < b->ModifyTime) t = -1;
else if (a->ModifyTime > b->ModifyTime) t = 1;
else t = 0;
}
break;
case 'N': /* Sort key is Name/Ext (default) */
t = strncmp((char *) a->Name, (char *) b->Name, 11);
break;
case 'E': /* Sort key is Ext/Name */
if ((t = strncmp(a->Ext, b->Ext, 3)) == 0) {
t = strncmp((char *) a->Name, (char *) b->Name, 8);
}
break;
case 'S': /* Sort key is File Size */
t = a->FileSize - b->FileSize;
break;
default:
t = strncmp((char *) a->Name, (char *) b->Name, 11);
break;
}
if (Inverse) t = -t; /* Sort order is inverse */
return ((t < 0) ? -1 : ((t > 0) ? 1 : 0));
}
unsigned
GetDosVersion (void) {
union REGS Regs;
Regs.h.ah = 0x30;
intdos(&Regs, &Regs);
return (Regs.x.ax);
}
void
Usage (void) {
printf("USAGE: CSAP [options] [[d:]directory_name]\n");
printf(" or\n");
printf(" CSAP [[d:]directory_name] [options]\n");
printf("\n");
printf("Options:\n");
printf(" -N Sort on Name/Ext (default).\n");
printf(" -D Sort on Date/Time.\n");
printf(" -E Sort on Ext/Name.\n");
printf(" -S Sort on File Size.\n");
printf("\n");
printf(" -R Report number of \"erased\" entries and directory location.\n");
printf(" -I Inverse sort order, i.e. descending.\n");
printf(" -P Do NOT remove \"erased\" entries.\n");
printf(" -L Limit sort to a single level.\n");
printf(" -V Request confirmation before sorting.\n");
printf(" -T Truncate directories.\n");
printf(" -H This message.\n");
exit(0);
}